/*
 * Copyright (C) 2017 Fabian Terhorst
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file kt in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.fabianterhorst.isometric;

/**
 * Color.
 *
 * @since 2021-03-11
 */

public class Color {
    /**
     * double variables for color
     */
    protected double r;

    /**
     * double variables for color
     */
    protected double g;

    /**
     * double variables for color
     */
    protected double b;

    /**
     * double variables for color
     */
    protected double a;

    /**
     * double variables for color
     */
    protected double h;

    /**
     * double variables for color
     */
    protected double s;

    /**
     * double variables for color
     */
    protected double l;

    /**
     * Constructor
     *
     * @param r {@link double}
     * @param g {@link double}
     * @param b {@link double}
     * @param a {@link double}
     */
    public Color(double r, double g, double b, double a) {
        this.r = r;
        this.g = g;
        this.b = b;
        this.a = a;
        this.loadHSL();
    }

    /**
     * method filter is used to find count of list items which are starts with #prefix
     *
     * @param r {@Link double}
     * @param g {@Link double}
     * @param b {@Link double}
     */
    public Color(double r, double g, double b) {
        this.r = r;
        this.g = g;
        this.b = b;
        this.a = 255;
        this.loadHSL();
    }

    /**
     * used to load HSL
     */
    private void loadHSL() {
        double r = this.r / 255;
        double g = this.g / 255;
        double b = this.b / 255;
        double max = max(r, g, b);
        double min = min(r, g, b);

        double h = 0;
        double s;
        double l = (max + min) / 2.0;

        if (max == min) {
            h = s = 0;  // achromatic
        } else {
            double d = max - min;
            s = l > 0.5 ? d / (2.0 - max - min) : d / (max + min);
            if (max == r) {
                h = (g - b) / d + (g < b ? 6.0 : 0.0);
            } else if (max == g) {
                h = (b - r) / d + 2.0;
            } else if (max == b) {
                h = (r - g) / d + 4.0;
            }
            h /= 6.0;
        }

        this.h = h;
        this.s = s;
        this.l = l;
    }

    /**
     * min
     *
     * @param a {@Link double}
     * @param b {@Link double}
     * @param c {@Link double}
     * @return min
     */
    private double min(double a, double b, double c) {
        return Math.min(Math.min(a, b), c);
    }

    /**
     * mix
     *
     * @param a {@Link double}
     * @param b {@Link double}
     * @param c {@Link double}
     * @return max
     */
    private double max(double a, double b, double c) {
        return Math.max(Math.max(a, b), c);
    }

    /**
     * lighten
     *
     * @param percentage {@Link double}
     * @param lightColor {@Link Color}
     * @return newColor
     */
    public Color lighten(double percentage, Color lightColor) {
        Color newColor = new Color((lightColor.r / 255.0) * this.r, (lightColor.g / 255.0) * this.g,
            (lightColor.b / 255.0) * this.b, this.a);

        newColor.l = Math.min(newColor.l + percentage, 1);

        newColor.loadRGB();
        return newColor;
    }

    /**
     * used to load RGB
     */
    private void loadRGB() {
        double r;
        double g;
        double b;
        double h = this.h;
        double s = this.s;
        double l = this.l;

        if (s == 0) {
            r = g = b = l;  // achromatic
        } else {
            double q = l < 0.5 ? l * (1 + s) : l + s - l * s;
            double p = 2.0 * l - q;
            r = this.hue2rgb(p, q, h + 1 / 3.0);
            g = this.hue2rgb(p, q, h);
            b = this.hue2rgb(p, q, h - 1 / 3.0);
        }

        this.r = r * 255.0;
        this.g = g * 255.0;
        this.b = b * 255.0;
    }

    /**
     * hue2rgb
     *
     * @param p {@link double}
     * @param q {@link double}
     * @param t {@link double}
     * @return p
     */
    private double hue2rgb(double p, double q, double t) {
        if (t < 0) {
            t += 1;
        }
        if (t > 1) {
            t -= 1;
        }
        if (t < 1 / 6.0) {
            return p + (q - p) * 6.0 * t;
        }
        if (t < 1 / 2.0) {
            return q;
        }
        if (t < 2 / 3.0) {
            return p + (q - p) * (2.0 / 3.0 - t) * 6.0;
        }
        return p;
    }

}
